<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>

  <script>
    // 修改视图的实际api, 因为vuejs 跨平台,所有不同平台操作方法不一致,需要统一
    const nodeOps = {
      createElement() { },
      // 插入某节点之前
      insertBefore() { },
      // 插入一个子节点
      appedChild() { },

      removeChild() { }

    }
    // 在父节点下插入一个子节点, 如果指定ref ,插入ref前
    function insert(parent, elm, ref) {
      if (parent) {
        if (ref) {
          if (res.parentNode === parent) {
            nodeOps.insertBefore(parent, elm, ref)
          }
        } else {
          nodeOps.appedChild(parent, elm)
        }
      }
    }
    // 创建一个新节点, tag存在-> 标签节点 , 否则创建文本节点
    function createElm(vnode, parentElm, refElm) {
      if (vnode.tag) {
        insert(parentElm, nodeOps.createElement(vnode.tag), refElm)
      } else {
        insert(parentElm, nodeOps.createElement(vnode.text), refElm)
      }
    }

    // 
    function addVnodes(parentElm, refElm, vnodes, startIdx, endIdx) {
      for (; startIdx < endIdx; startIdx++) {
        createElm(vnodes[startIdx], parentElm, refElm)
      }
    }

    function removeNode(el) {
      const parent = nodeOps.parentNode(el)
      if (parent) {
        nodeOps.removeChild(parent, el)
      }
    }

    function removeNodes(parentElm, vnodes, startIdx, endIdx) {
      for (; startIdx < endIdx; startIdx++) {
        const el = vnodes[startIdx]
        if (el) {
          removeNode(el.elm)
        }
      }
    }

    // patch 

    function patch(oldVnode, vnode, parentElm) {
      if (!oldVnode) { // 如果旧节点不存在, 第一次渲染, 创建节点
        addVnodes(parentElm, null, vnode, 0, vnode.length - 1);
      } else if (!vnode) { // 新的vnode 不存在,说明是移除操作
        removeVnodes(parentElm, oldVnode, 0, oldVnode.length - 1);
      } else {
        if (sameVnode(oldVNode, vnode)) { // 是否值得比较
          patchVnode(oldVNode, vnode);
        } else { // 如果不是,移除就的,渲染新的
          removeVnodes(parentElm, oldVnode, 0, oldVnode.length - 1);
          addVnodes(parentElm, null, vnode, 0, vnode.length - 1);
        }
      }
    }

    function sameVnode(a, b) {
      return (a.key === b.key && a.tag === b.tag && a.isComment === b.isComment && (!!a.data) === (!!b.data) && sameInputType(a, b))
    }

    function sameInputType(a, b) {
      if (a.tag !== 'input') return true
      let i
      const typeA = (i = a.data) && (i = i.attrs) && i.type
      const typeB = (i = b.data) && (i = i.attrs) && i.type
      return typeA === typeB
    }


    function patchVnode(oldVnode, vnode) {
      if (oldVnode === vnode) { // 新旧节点一样, 不需要比对
        return;
      }

      if (vnode.isStatic && oldVnode.isStatic && vnode.key === oldVnode.key) { // 如果是静态节点,key 一样 , 直接把旧节点赋值给新节点
        vnode.elm = oldVnode.elm;
        vnode.componentInstance = oldVnode.componentInstance;
        return;
      }

      const elm = vnode.elm = oldVnode.elm;
      const oldCh = oldVnode.children;
      const ch = vnode.children;

      if (vnode.text) { // vnode如果是文本节点, 直接修改节点内容
        nodeOps.setTextContent(elm, vnode.text);
      } else {
        if (oldCh && ch && (oldCh !== ch)) {
          updateChildren(elm, oldCh, ch);
        } else if (ch) {
          if (oldVnode.text) nodeOps.setTextContent(elm, '');
          addVnodes(elm, null, ch, 0, ch.length - 1);
        } else if (oldCh) {
          removeVnodes(elm, oldCh, 0, oldCh.length - 1)
        } else if (oldVnode.text) {
          nodeOps.setTextContent(elm, '')
        }
      }
    }
  </script>

</body>

</html>